﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
using System.IO;
using ListIt.DAL;
using System.Security.Cryptography;
using System.Web;
using System.Security.Cryptography.X509Certificates; // used for Geocoding
using System.Threading;
using System.Diagnostics;
using System.Net;
using C2dmSharp.Server;
using System.Net.Mail;
using System.Net.Sockets;

namespace ListItTaxi.Web
{
	[ServiceContract(Namespace = "ListIt")]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
	public class SmartService : IDisposable
	{
		public SmartService()
		{
			/*FileStream objStream = new FileStream(@"C:\Logs\SmartService.log", FileMode.OpenOrCreate);
			TextWriterTraceListener objTraceListener = new TextWriterTraceListener(objStream);
			Trace.Listeners.Add(objTraceListener);
			LogEvent("Started");*/

			// setup Apple Push Notification
			//string certificatePath = HttpRuntime.AppDomainAppPath + @"Certificate\apn_developer_identity.P12";
			//string certificatePassword = "3425batibot";

			//X509Certificate2 clientCertificate = new X509Certificate2(certificatePath, certificatePassword);
			//_APSN = new iPhoneAPNS(clientCertificate);
		}
		
		#region IDisposable Members

		public void Dispose()
		{
			LogEvent("Disposed");
			Trace.Close();

			//_DAL.Dispose();
		}

		#endregion

		#region Constants
        private static string MASTER_PWD = "mini426kaniko";

		private static string RET_MESSAGE = "message";
        private static string RET_MESSAGE_INNER = "message_inner";
		private static string RET_CODE = "return_code";
		private static string RET_HTTP_CODE = "http_code";		
        private static string RET_C2DM_EMAIL = "c2dm_email";
        private static string RET_PICKUP = "pickup";
        private static string RET_DESTINATION ="destination";
        private static string RET_TEL_NO = "telno";
        private static string RET_MOBILE = "mobile";
        private static string RET_SPECIAL_INSTRUCTION = "info";
        private static string RET_DEBUG = "debug";
        private static string RET_LONGITUDE = "long";
        private static string RET_LATITUDE = "lat";
        private static string RET_CUSTOMER = "customer";
		#endregion

        private void AddExceptionToJSON(Exception e, JSONObject json)
        {
            json.add(RET_CODE, ((int)WEBSERVER_MESSAGES.SERVER_ERROR).ToString());
            json.add(RET_MESSAGE, e.Message);

            if (e.InnerException != null)
                json.add(RET_MESSAGE_INNER, e.InnerException.Message);
        }

        private void LogEvent(string s)
		{
			//Trace.WriteLine(string.Format("[{0}]\t{1}", DateTime.Now, s));
			//Trace.Flush();
		}

		private void SetLoginStatus(bool loggedIn, Taxi taxi, string deviceID)
		{
			using (ListItDAL DAL = new ListItDAL())
			{
				if (taxi == null) {
					taxi = DAL.ListItEntities.Taxis.SingleOrDefault(a => a.DeviceID == deviceID);
				}
				
				taxi.LoggedIn = loggedIn;

                if (!loggedIn)
                    taxi.Available = false;

				DAL.ListItEntities.SaveChanges();
			}
		}		

		private byte[] StringToBytes(string strInput)
		{
			// Assume string is in this format "######## ######## ######## ######## ######## ######## ######## ########"
			System.Diagnostics.Debug.Assert(((strInput.Length + 1) % 9) == 0);

			int groupCount = (strInput.Length + 1) / 9;
			int byteCount = groupCount * 4;

			byte[] bytes = new byte[byteCount];
			for (int i = 0; i < byteCount; i++)
			{
				int offset = i * 2 + (i / 4);
				bytes[i] = Convert.ToByte(strInput.Substring(offset, 2), 16);
			}
			return bytes;
		}

		private string FormReturnString(string func, int statusCode, string message)
		{
			return string.Format("{{\"{0}\":{{\"status\":{1}, \"message\":\"{2}\"}}}}", func, 
				statusCode, message);
		}

		private string FormReturnString(string func, HttpStatusCode statusCode)
		{
			return FormReturnString(func, (int)statusCode, "OK");
		}

        private bool HasJob(Taxi taxi)
        {
            using (ListItDAL DAL = new ListItDAL())
            {
                var jobs = from jobBacklog in DAL.ListItEntities.JobBackLogs
                           where (jobBacklog.ID == taxi.JobPending)
                           select jobBacklog;

                /* Look for the job in the backlog */
                if (jobs.Count() > 0)
                    return true;
                else
                {
                    /* If no job in the backlog, check the active jobs */
                    ActiveJob activeJob = DAL.ListItEntities.ActiveJobs.SingleOrDefault(a => a.LicenseNo.Trim() == taxi.LicenseNo.Trim());

                    if (activeJob != null)
                        return true;
                }
            }	

            return false;
        }

        public string DispatchJob(JobBackLog job, ListItDAL dal)
        {
            LogEvent("Dispatching Job");
            string result;
            while (true)
            {
                var collection = from taxis in dal.ListItEntities.Taxis
                                 where (taxis.Available == true) && (taxis.JobPending == -1) && (taxis.VehicleType == job.VehicleType)
                                 select taxis;

                List<Taxi> taxiList = collection.ToList();
                Taxi suitableTaxi = null;
                double distanceBetween = 99999999; // kilometers
                double temp;

                List<string> declinedDrivers;
                if (job.DeclinedDrivers != null)
                    declinedDrivers = job.DeclinedDrivers.Split(',').ToList();
                else
                    declinedDrivers = new List<string>();

                foreach (Taxi taxi in taxiList)
                {
                    // now we need to look for a suitable taxi - closest and available                       
                    if (declinedDrivers.IndexOf(taxi.LicenseNo.Trim()) == -1)
                    {
                        // this driver hasn't declined the job yet                                        
                        temp = ListIt.DAL.GeoStaicClass.distance(Convert.ToDouble(taxi.Latitude), Convert.ToDouble(taxi.Longituted),
                            Convert.ToDouble(job.PickupLatitude), Convert.ToDouble(job.PickupLongituted), 'K');

                        if (temp < distanceBetween)
                        {
                            suitableTaxi = taxi;
                            distanceBetween = temp;
                        }
                    }
                }

                // if there is a suitable taxi, then lets dispatch to that
                if (suitableTaxi != null)
                {
                    // mark pending driver so that we don't dispatch this job again, unless the driver declines
                    job.PendingDriver = true;
                    suitableTaxi.JobPending = job.ID;
                    dal.ListItEntities.SaveChanges();
                    //_APSN.PushCombinedNotification(StringToBytes(suitableTaxi.DeviceID), "You have a job request!", null, "default");

                    // then our work here is done!
                    result = "OK";
                    return result;
                }

                result = "No suitable table";

                LogEvent(result);
                Thread.Sleep(10000); // sleep for 10 seconds
            }
        }

		#region Operational Contracts
		[OperationContract]
		[WebGet()]
		public string DoWork()
		{
			LogEvent("Doing Work");

            JSONObject result = new JSONObject("DoWork");
            result.add(RET_MESSAGE, "if (!sleep) { sleep++; }");
            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());

            return result.ToString();
		}

        [OperationContract]
        [WebGet()]
        public string DoError()
        {
            JSONObject result = new JSONObject("DoWork");
            try
            {
                throw new Exception("This is an example of an exception");
            }
            catch (Exception e)
            {
                AddExceptionToJSON(e, result);
            }
            
            return result.ToString();
        }
        
        [OperationContract]
        [WebGet()]
        public string Decrypt(string str)
        {
            SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
            return simpleEncryption.DecryptFromBase64(str);
        }

		[OperationContract]
		[WebGet()]
		public string RequestJob(string deviceID, string customerName, string mobileNo, string telNo, string pickup, string specialInstruction, string destination,
			double travelDate, int taxiCoy, short vehicleType)
		{
			LogEvent(string.Format("Requesting job: DeviceID={0}", deviceID));
            JSONObject result = new JSONObject("RequestJob");

			using (ListItDAL DAL = new ListItDAL()) {
				DAL.ListItEntities.Refresh(System.Data.Objects.RefreshMode.StoreWins,
					DAL.ListItEntities.JobBackLogs);

				// RequestJob?deviceID=&customerName=Allan&mobileNo=123&tel=123&pickup=sdfdf&specialInstruction=foo&destination=sdfjdf&44&taxiCoy=1&vehicleType=1
				try
				{
					Coordinate coord = Google.GeoCode(pickup);

					if (coord == null)
					{
						coord = Google.GeoCode("Christchurch city, new zealand");
					}

					if (coord == null)
						throw new Exception("Unable to Geocode");

					// check if the job alread exists in the back log for orders that come from the device
					if (deviceID != "")
					{
						// var jobBacklog = from jobs in _DAL.ListItEntities.JobBackLogs
						//                  where jobs.DeviceID == deviceID
						//                  select jobs;

						// we might need to do some job checking here to prevent server flooding 
					}

					DateTime dt;

					if (travelDate == 0)
						dt = DateTime.Now;
					else
						dt = DateTime.FromOADate(travelDate);

					JobBackLog job = DAL.AddJobToBackLog(deviceID == "" ? "ABC123" : deviceID, customerName, pickup, specialInstruction, destination,
						dt, mobileNo, telNo, vehicleType, taxiCoy, coord.Longitude, coord.Latitude);

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
				}
				catch (Exception e)
				{
                    AddExceptionToJSON(e, result);
				}
			}

            return result.ToString();
		}
		
		[OperationContract]
		[WebGet()]
		public string SetStatus(string deviceID, string isBusy)
		{
            JSONObject result = new JSONObject("SetStatus");
			try {
				using (ListItDAL DAL = new ListItDAL())
				{
                    string clearIsBusy = isBusy; // if Taxi is null then isBusy is not encrypted because the call came internally
			
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                    clearIsBusy = simpleEncryption.DecryptFromBase64(isBusy);
                    Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(a => a.DeviceID == clearDeviceID);
              

                    if ((bool)taxi.LoggedIn)
                    {
                        taxi.Available = clearIsBusy == "false";
                        taxi.JobPending = -1;
                        taxi.TimeStatusChange = DateTime.Now;
                        //taxi.DeviceToken = deviceToken;
                        DAL.ListItEntities.SaveChanges();

                        if (clearIsBusy == "true")
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.STATUS_BUSY).ToString());
                        else
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.STATUS_AVAILABLE).ToString());
                    }
                    else
                    {
                        result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.DEVICE_NOT_LOGGED_IN).ToString());
                    }
					
					result.add(RET_MESSAGE, "");
				}
            }
			catch (Exception e) {
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string GetStatus(string deviceID)
		{
			JSONObject result = new JSONObject("GetStatus");

			try {
				using (ListItDAL DAL = new ListItDAL())
				{
					SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
					string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
					
					Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(a => a.DeviceID == clearDeviceID);
					if (taxi != null)
					{
						if ((bool)taxi.LoggedIn)
						{   
							if (taxi.Available)
								result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.STATUS_AVAILABLE).ToString());
							else
								result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.STATUS_BUSY).ToString());
						}   
						else
						{
							result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.DEVICE_NOT_LOGGED_IN).ToString());
							result.add(RET_MESSAGE, string.Format("You are not currently logged in!"));
						}
					}
					else
						throw new Exception(string.Format("Failed to retrieve taxi with device ID of {0}",
							clearDeviceID));
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

			return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string GetLoginStatus(string deviceID)
		{
			JSONObject result = new JSONObject("GetLoginStatus");
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
					SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
					string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(a => a.DeviceID == clearDeviceID);
					if (taxi != null)
					{
						result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
					}
					else
						throw new Exception(string.Format("Failed to retrieve taxi with device ID of {0}",
							clearDeviceID));
				}
			}
			catch (Exception e)
			{
				result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.SERVER_ERROR).ToString());
				result.add(RET_MESSAGE, e.Message);

			}

			return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string DeclineJob(string deviceID)
		{
            JSONObject result = new JSONObject("DeclineJob");
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    var taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID.Trim() == clearDeviceID);

					if (taxi != null)
					{
						var job = DAL.ListItEntities.JobBackLogs.SingleOrDefault(j => j.ID == taxi.JobPending);

						if (job != null)
						{
							// remove driver from job
							job.PendingDriver = false;
							string s = job.DeclinedDrivers;
							List<string> declinedDrivers;
							if (s != null)
								declinedDrivers = job.DeclinedDrivers.Split(',').ToList();
							else
								declinedDrivers = new List<string>();

							declinedDrivers.Add(taxi.LicenseNo.Trim());
							job.DeclinedDrivers = string.Join(",", declinedDrivers);

							// set taxi status so driver is no longer busy or has a job pending
							taxi.Available = true;
							taxi.JobPending = -1;
                            job.DispatchDate = null;

							DAL.ListItEntities.SaveChanges();
						}
					}
                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
                    return result.ToString();
				}
			}
			catch (Exception e)
			{
				return FormReturnString("DeclineJob", (int)HttpStatusCode.InternalServerError, e.Message);
			}
		}

		[OperationContract]
		[WebGet()]
		public string WithdrawJob(string deviceID)
		{
            JSONObject result = new JSONObject("WithdrawJob");
			try
			{
				using (ListItDAL DAL = new ListItDAL()) {
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    var taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID == clearDeviceID);

					if (taxi != null)
					{
						var job = DAL.ListItEntities.ActiveJobs.SingleOrDefault(a => a.LicenseNo.Trim() == taxi.LicenseNo.Trim());

						if (job != null)
						{
							string custDevID = job.DeviceID;
							string customerName = job.CustomerName;
							string pickupAddress = job.PickupAddress;
							string destinationAddress = job.DestinationAddress;
							string specialInstruction = job.SpecialInstruction;
							string mobileNo = job.MobileNo;
							string telNo = job.TelNo;
							short vehicleType = Convert.ToInt16(taxi.VehicleType);
							string s = job.DeclinedDrivers;
							int taxiCoy = 1;

							Coordinate coord = Google.GeoCode(pickupAddress);

							// set taxi status so driver is no longer busy or has a job pending
							taxi.Available = true;
							taxi.JobPending = -1;

							DAL.ListItEntities.ActiveJobs.DeleteObject(job);

							var jobBackLog = DAL.AddJobToBackLog(custDevID, customerName, pickupAddress, specialInstruction, destinationAddress, DateTime.Now,
								mobileNo, telNo, vehicleType,
								taxiCoy, coord.Longitude, coord.Latitude);

							List<string> declinedDrivers;
							if (s != null)
								declinedDrivers = s.Split(',').ToList();
							else
								declinedDrivers = new List<string>();

							declinedDrivers.Add(taxi.LicenseNo.Trim());
							jobBackLog.DeclinedDrivers = string.Join(",", declinedDrivers);

							DAL.ListItEntities.SaveChanges();

							//DispatchJob(jobBackLog, DAL);
						}
					}

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_WITHDRAW).ToString());
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string TerminateJob(string deviceID, string reason)
		{
            JSONObject result = new JSONObject("TerminateJob");
			try
			{
				using (ListItDAL DAL = new ListItDAL()) {
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    var taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID == clearDeviceID);

					if (taxi != null)
					{
						var job = taxi.ActiveJobs.Single();
						var jobHistory = DAL.AddJobToHistory(job);
						jobHistory.Comments = reason;
						DAL.ListItEntities.ActiveJobs.DeleteObject(job);
						taxi.Available = true;
						taxi.JobPending = -1;
						DAL.ListItEntities.SaveChanges();
					}

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_TERMINATE).ToString());
                    return result.ToString();
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
        }

		[OperationContract]
		[WebGet()]
		public string GetJob(string deviceID)
		{
			// When this method is called, it means that the driver have accepted the job
			//string result = "";
            JSONObject result = new JSONObject("GetJob");
			try
			{
                using (ListItDAL DAL = new ListItDAL())
                {

                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    var collection = from taxis in DAL.ListItEntities.Taxis
                                     where (taxis.DeviceID == clearDeviceID)
                                     select taxis;

                    List<Taxi> taxiList = collection.ToList();
                    ActiveJob activeJob = null;

                    foreach (Taxi taxi in taxiList)
                    {
                        var jobs = from jobBacklog in DAL.ListItEntities.JobBackLogs
                                   where (jobBacklog.ID == taxi.JobPending)
                                   select jobBacklog;

                        List<JobBackLog> jobList = jobs.ToList();

                        /* Look for the job in the backlog */
                        if (jobList.Count > 0)
                        {
                            foreach (JobBackLog job in jobList)
                            {
                                // this job needs to go to active table
                                activeJob = DAL.AddJobToActive(job, taxi.LicenseNo.Trim());

                                // now we no longer need the record in the job backlog
                                DAL.ListItEntities.JobBackLogs.DeleteObject(job);

                                // the taxi also doesn't need to know about the pending job
                                taxi.JobPending = -1;

                                // this taxi is no longer available
                                taxi.Available = false;

                                DAL.ListItEntities.SaveChanges();
                            }
                        }
                        else /* If no job in the backlog, check the active jobs */
                            activeJob = DAL.ListItEntities.ActiveJobs.SingleOrDefault(a => a.LicenseNo.Trim() == taxi.LicenseNo.Trim());
                    }

                    if (activeJob != null)
                    {
                        //string message = DAL.GetSerializedJob(activeJob);
                        if (activeJob.PickupAddress != null)
                            result.add(RET_PICKUP, activeJob.PickupAddress);
                        else
                            result.add(RET_PICKUP, "NOT DEFINED");

                        if (activeJob.DestinationAddress != null)
                            result.add(RET_DESTINATION, activeJob.DestinationAddress);
                        else
                            result.add(RET_DESTINATION, "NOT DEFINED");

                        if (activeJob.MobileNo != null)
                            result.add(RET_MOBILE, activeJob.MobileNo.ToString());
                        else
                            result.add(RET_MOBILE, "");

                        if (activeJob.TelNo != null)
                            result.add(RET_TEL_NO, activeJob.TelNo.ToString());
                        else
                            result.add(RET_TEL_NO, "");

                        if (activeJob.SpecialInstruction != null)
                            result.add(RET_SPECIAL_INSTRUCTION, activeJob.SpecialInstruction);
                        else
                            result.add(RET_SPECIAL_INSTRUCTION, "");

                        if (activeJob.PickupLongituted != null)
                            result.add(RET_LONGITUDE, activeJob.PickupLongituted.ToString());

                        if (activeJob.PickupLatitude != null)
                            result.add(RET_LATITUDE, activeJob.PickupLatitude.ToString());

                        if ((activeJob.CustomerName == "Default Admin") || (activeJob.CustomerName == ""))
                            result.add(RET_CUSTOMER, "No name provided");
                        else
                            result.add(RET_CUSTOMER, activeJob.CustomerName);

                        result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
                    }
                    else
                    {
                        result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_EXPIRED).ToString());
                    }
                }				
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
		}

        [OperationContract]
        [WebGet()]
        // NOTE: check if this is still used
        public string HasBacklogJob(string deviceID)
        {
            JSONObject result = new JSONObject("HasBacklogJob");

            // When this method is called, it means that the driver have accepted the job
            try
            {
                using (ListItDAL DAL = new ListItDAL())
                {

                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

                    var collection = from taxis in DAL.ListItEntities.Taxis
                                     where (taxis.DeviceID == clearDeviceID)
                                     select taxis;

                    List<Taxi> taxiList = collection.ToList();

                    foreach (Taxi taxi in taxiList)
                    {
                        var jobs = from jobBacklog in DAL.ListItEntities.JobBackLogs
                                   where (jobBacklog.ID == taxi.JobPending)
                                   select jobBacklog;

                        List<JobBackLog> jobList = jobs.ToList();

                        /* Look for the job in the backlog */
                        if (jobList.Count > 0)
                        {
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.HAS_JOB).ToString());
                        }
                        else
                        {
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_EXPIRED).ToString());
                        }
                    }
                }
            }
            catch (Exception e)
            {
                AddExceptionToJSON(e, result);
            }

            return result.ToString();
        }

		[OperationContract]
		[WebGet()]
        // NOTE: check if this is still used
		public string HasActiveJob(string deviceID)
		{
            JSONObject result = new JSONObject("HasActiveJob");

			// When this method is called, it means that the driver have accepted the job
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{

                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

					var collection = from taxis in DAL.ListItEntities.Taxis
                                     where (taxis.DeviceID == clearDeviceID)
									 select taxis;

					List<Taxi> taxiList = collection.ToList();
					ActiveJob activeJob = null;

					foreach (Taxi taxi in taxiList)
					{
                        /* If not in the backlog, check the active jobs */
                        activeJob = DAL.ListItEntities.ActiveJobs.SingleOrDefault(a => a.LicenseNo.Trim() == taxi.LicenseNo.Trim());
                        if (activeJob != null)
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.HAS_JOB).ToString());
                        else
                            result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_EXPIRED).ToString());
					}
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string CompleteJob(string deviceID)
		{
            JSONObject result = new JSONObject("CompleteJob");
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID); 

					var obj = from taxis in DAL.ListItEntities.Taxis
                              where (taxis.DeviceID == clearDeviceID)
							  select taxis;

					List<Taxi> taxiList = obj.ToList();

					foreach (Taxi taxi in taxiList)
					{
						var jobs = from activeJob in DAL.ListItEntities.ActiveJobs
								   where (activeJob.LicenseNo.Trim() == taxi.LicenseNo.Trim())
								   select activeJob;

						List<ActiveJob> jobList = jobs.ToList();

						foreach (ActiveJob job in jobList)
						{
							// we need to move the active job to historys
							DAL.AddJobToHistory(job);

							// this job is no longer required in the active job table
							DAL.ListItEntities.ActiveJobs.DeleteObject(job);

							// free up the taxi
							taxi.Available = true;
							taxi.JobPending = -1;

							DAL.ListItEntities.SaveChanges();
						}
					}

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.JOB_COMPLETE).ToString());                    
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

            return result.ToString();
		}

		[OperationContract]
		[WebGet()]
        // NOTE: check if this is still used
		public bool Authenticate(string username, string password)
		{
			using (ListItDAL DAL = new ListItDAL())
			{
				return DAL.Authenticate(username, password);
			}
		}

		[OperationContract]
		[WebGet()]
		public string Logout(string deviceID)
		{
			LogEvent(string.Format("Logging Out: DeviceID={0}", deviceID));

			JSONObject result = new JSONObject("Logout");
			try
			{
				// decrypt device ID
				SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
				string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);

				// set driver's status
				this.SetLoginStatus(false, null, clearDeviceID);

				// prep return JSON
				result.add(RET_MESSAGE, "");
				result.add(RET_CODE, ((int)ListIt.DAL.WEBSERVER_MESSAGES.LOGGED_OUT).ToString());
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);				
			}

            return result.ToString();
		}

		[OperationContract]
		[WebGet()]
		public string Pagsusuri9S5T67KJHoiil56(string deviceID, string deviceToken, string username, string password)
		{
			LogEvent(string.Format("Logging In: DeviceID={0}", deviceID));

			JSONObject result = new JSONObject("Pagsusuri9S5T67KJHoiil56");
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
					string message = null;

					SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);

					string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
					string clearUsername = simpleEncryption.DecryptFromBase64(username);
					string clearPassword = simpleEncryption.DecryptFromBase64(password);
                    string clearDeviceToken = simpleEncryption.DecryptFromBase64(deviceToken);

					int retCode = DAL.AuthenticateTaxi(clearDeviceID, clearUsername, clearPassword, out message);
                    bool debug = false;

					if (message == "")
					{
						var taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID == clearDeviceID);
     
                        taxi.DeviceToken = clearDeviceToken;
                        taxi.LoggedIn = true;
                        taxi.Available = !HasJob(taxi);

                        // get the debug flag so we can send it to the problematic device
                        if (taxi.Debug == true)
                            debug = true;

                        DAL.ListItEntities.SaveChanges();
					}

					result.add(RET_MESSAGE, message);
					result.add(RET_CODE, retCode.ToString());
                    result.add(RET_C2DM_EMAIL, ProtocolConstants.C2DM_SENDER);
                    result.add(RET_DEBUG, debug.ToString());
				}
			}
			catch (Exception e)
			{
                AddExceptionToJSON(e, result);
			}

			return result.ToString();
		}

		[OperationContract]
		[WebGet()]
        // NOTE: check if this is still used
		public string AddUser(string username, string password, string email, string passPhrase, string answer)
		{
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
					string result = DAL.AddUser(username, password, email, passPhrase, answer);
					return FormReturnString("AddUser", (int)HttpStatusCode.OK, result);
				}
			}
			catch (Exception e)
			{
				return FormReturnString("AddUser", (int)HttpStatusCode.InternalServerError, e.Message);
			}
		}

		[OperationContract]
		[WebGet()]
		public string RegisterCustomerDevice(string deviceID, string customerName, string mobileNo)
		{
			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{

					DAL.RegisterCustomerDevice(deviceID, customerName, mobileNo);
					return FormReturnString("RegisterCustomerDevice", HttpStatusCode.OK);
				}
			}
			catch (Exception e)
			{
				return FormReturnString("RegisterCustomerDevice", (int)HttpStatusCode.InternalServerError,
					e.Message);
			}
		}

		[OperationContract]
		[WebGet()]
		public string UpdateLocation(string deviceID, string longitude, string latitude)
		{
            JSONObject result = new JSONObject("UpdateLocation");
            bool taxiInDebug = false;

			try
			{
				using (ListItDAL DAL = new ListItDAL())
				{
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                    string clearLong = simpleEncryption.DecryptFromBase64(longitude);
                    string clearLat = simpleEncryption.DecryptFromBase64(latitude);

                    Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID == clearDeviceID);

					if (taxi != null)
					{
                        taxiInDebug = taxi.Debug == true;

						taxi.Longituted = Convert.ToDecimal(clearLong);
						taxi.Latitude = Convert.ToDecimal(clearLat);
						DAL.ListItEntities.SaveChanges();
					}

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
				}
			}
			catch (Exception e)
			{
                if (taxiInDebug)
                    AddExceptionToJSON(e, result);
                else
                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString()); // do not return error unless device is in debug. This fn is called too often
			}

            return result.ToString();
		}

        [OperationContract]
        [WebGet()]
        public string UpdateC2DMKey(string deviceID, string token)
        {
            JSONObject result = new JSONObject("UpdateC2DMKey");
            try
            {
                using (ListItDAL DAL = new ListItDAL())
                {
                    // decrypt device ID & Token
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                    string clearToken = simpleEncryption.DecryptFromBase64(token);

                    Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(a => a.DeviceID == clearDeviceID);
                    if (taxi != null)
                    {
                        taxi.DeviceToken = clearToken;
                        DAL.ListItEntities.SaveChanges();
                    }
                    else
                        throw new Exception(string.Format("Failed to retrieve taxi with device ID of {0}",
                            clearDeviceID));
                }

                // prep return JSON
                result.add(RET_MESSAGE, "");
                result.add(RET_CODE, ((int)ListIt.DAL.WEBSERVER_MESSAGES.OK).ToString());
            }
            catch (Exception e)
            {
                AddExceptionToJSON(e, result);
            }

            return result.ToString();
        }

        [OperationContract]
        [WebGet()]
        public string GetC2DMEmail()
        {
            JSONObject result = new JSONObject("GetC2DMEmail");
            try
            {
                // prep return JSON
                result.add(RET_C2DM_EMAIL, ProtocolConstants.C2DM_SENDER);
                result.add(RET_CODE, ((int)ListIt.DAL.WEBSERVER_MESSAGES.OK).ToString());                
            }
            catch (Exception e)
            {
                AddExceptionToJSON(e, result);
            }

            return result.ToString();
        }

        [OperationContract]
        [WebGet()]
        public string ReportUnhandledError(string deviceID, string stackTrace)
        {
            try
            {
                SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                string clearStackTrace = simpleEncryption.DecryptFromBase64(stackTrace);

               //Send the message.
                SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
                client.Credentials = new NetworkCredential("taxivalet.listit@gmail.com", "Allan2010");
                client.EnableSsl = true;

                client.Send("taxivalet.listit@gmail.com",
                    "si_chinito@hotmail.com", "TaxiValet Unhandled Error - " + clearDeviceID,
                    clearStackTrace);
            }
            catch (Exception e)
            {
                return e.Message;
            }

            return "ok";
        }

        [OperationContract]
        [WebGet()]
        public string UpdateIPAddress(string deviceID, string ipAddress)
        {
            JSONObject result = new JSONObject("UpdateLocation");
            bool taxiInDebug = false;

            try
            {
                using (ListItDAL DAL = new ListItDAL())
                {
                    SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                    string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                    string clearIP = simpleEncryption.DecryptFromBase64(ipAddress);

                    Taxi taxi = DAL.ListItEntities.Taxis.SingleOrDefault(t => t.DeviceID == clearDeviceID);

                    if (taxi != null)
                    {
                        taxi.IPAddress = clearIP;
                        DAL.ListItEntities.SaveChanges();
                    }

                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString());
                }
            }
            catch (Exception e)
            {
                if (taxiInDebug)
                    AddExceptionToJSON(e, result);
                else
                    result.add(RET_CODE, ((int)WEBSERVER_MESSAGES.OK).ToString()); // do not return error unless device is in debug. This fn is called too often
            }

            return result.ToString();
        }

        [OperationContract]
        [WebGet()]
        public string HailCab(string deviceID, string latitude, string longitude, string azimuth)
        {
            // HailCab?deviceID=87cUuPM5qha6hx59U1ZCoQ%3D%3D&latitude=Ez1%2BuRSwxsc%2FgvIAfuAWzQ%3D%3D&longitude=8cwWxU7n4wa%2F013I%2FrC73w%3D%3D&azimuth=WNwWDK5XD%2FTgNPmwDdirtw%3D%3D
            try
            {
                SimpleEncryption simpleEncryption = new SimpleEncryption(MASTER_PWD);
                string clearDeviceID = simpleEncryption.DecryptFromBase64(deviceID);
                double clearLatitude = Convert.ToDouble(simpleEncryption.DecryptFromBase64(latitude));
                double clearLongitude = Convert.ToDouble(simpleEncryption.DecryptFromBase64(longitude));
                double clearAzimuth = Convert.ToDouble(simpleEncryption.DecryptFromBase64(azimuth));

                using (ListItDAL DAL = new ListItDAL())
                {
                    var collection = from taxis in DAL.ListItEntities.Taxis
                                     where (taxis.Available == true) && (taxis.JobPending == -1)
                                     orderby taxis.TimeStatusChange
                                     select taxis;

                    List<Taxi> taxiList = collection.ToList();
                    List<Taxi> taxisInRange = new List<Taxi>();
                    double taxiDistance;

                    // go through all available taxis
                    foreach (Taxi taxi in taxiList)
                    {
                        taxiDistance = ListIt.DAL.GeoStaicClass.distance(Convert.ToDouble(taxi.Latitude), 
                            Convert.ToDouble(taxi.Longituted),
                         clearLatitude, clearLongitude, 'K');

                        // build a pool of suitable taxis
                        if (taxiDistance <= .1)
                        {
                            taxisInRange.Add(taxi);
                        }
                    }

                    // we need to interpolate the point towards the azimuth
                    Coordinate interpolatedPoints = Coordinate.InterpolateAzimuth(
                        clearLatitude, clearLongitude, clearAzimuth, .1);

                    // workout the bearing from customer to interpolated point
                    //double aBearing = Coordinate.Bearing(interpolatedPoints.Longitude - clearLongitude,
                    //    clearLatitude, interpolatedPoints.Latitude);

                    List<Taxi> coneOfCertainty = new List<Taxi>();

                    foreach (Taxi taxi in taxisInRange)
                    {
                        double taxiLong = taxi.Longituted != null ? (double)taxi.Longituted : 0;
                        double taxiLat = taxi.Latitude != null ? (double)taxi.Latitude : 0;

                        // workout in our taxirange who falls in the cone of certainty
                        double tBearing = Coordinate.Bearing(taxiLong - clearLongitude,
                            clearLatitude, taxiLat);

                        /* 
                         * |orginin - taxi| =  distance
                         * if distance is > 360 do some correction
                         */

                        tBearing = tBearing != 0 ? tBearing : clearAzimuth;

                        double margin = Math.Abs(clearAzimuth - tBearing);
                        if (margin > 180)
                            margin = 360 - margin;

                        if (margin <= 45) // 10 degress tolerance
                            coneOfCertainty.Add(taxi);
                    }

                    // workout who is closest
                    Taxi closestTaxi = null;
                    double currentDistance = .1;

                    foreach (Taxi taxi in coneOfCertainty)
                    {
                        taxiDistance = ListIt.DAL.GeoStaicClass.distance(Convert.ToDouble(taxi.Latitude), 
                            Convert.ToDouble(taxi.Longituted),
                        clearLatitude, clearLongitude, 'K');

                        if (taxiDistance <= currentDistance)
                        {
                            currentDistance = taxiDistance;
                            closestTaxi = taxi;
                        }
                    }

                    #region dispatch
                    if (closestTaxi != null)
                    {
                        IPAddress ipAddress = IPAddress.Parse(closestTaxi.IPAddress);
                        IPEndPoint endPoint = new IPEndPoint(ipAddress, 53709);
                        TcpClient tcpClient = new TcpClient();
                        try
                        {
                            tcpClient.ExclusiveAddressUse = true;
                            tcpClient.Connect(endPoint);
                        }
                        finally
                        {
                            tcpClient.Client.Shutdown(SocketShutdown.Both);
                            tcpClient.Close();
                        }
                    }
                    #endregion
                }


                //Send the message.
            }
            catch (Exception e)
            {
                return e.Message;
            }

            return "ok";
        }
        #endregion
	}      
}
